-- | Module:        Hexena.OptParse
--   Stability:     Experimental
--   Portability:   Portable
--   
--   The command line multimode option parser for Hexena.
--
--   LANGUAGE Pragma's DeriveDataTypeable and RecordWildCards
--   are required to use the CmdArgs library.  Well just
--   DeriveDataTypeable but RecordWildCards definitely 
--   saves on your typing, and hence possible mistakes.
--
-- http://community.haskell.org/~ndm/darcs/cmdargs/cmdargs.htm
-- http://hackage.haskell.org/packages/archive/cmdargs/latest/doc/html/System-Console-CmdArgs-Implicit.html#v:args

{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
module Hexena.OptParse 
    ( optionHandler
    , rootMode
    ) where

import System.Console.CmdArgs

import System.Environment (getArgs, withArgs)
import System.Exit

import Control.Monad (when)

import Hexena.OptParse.Internal ( _PROGRAM_NAME
                                , _PROGRAM_VERSION
                                , _PROGRAM_INFO
                                , _PROGRAM_ABOUT
                                )
                                 
import qualified Hexena.Cmds.BlockDraw (run)
import qualified Hexena.Cmds.CalcCV (run)
import qualified Hexena.Cmds.CalcBlockEntropy (run)
import qualified Hexena.Cmds.ClassifySVM (run)
import qualified Hexena.Cmds.EncapsulateEXE (run)
import qualified Hexena.Cmds.EntropyCalc (run)
import qualified Hexena.Cmds.EntropyXPM (run)
import qualified Hexena.Cmds.GenerateReport (run)
import qualified Hexena.Cmds.GetSVMData (run)
import qualified Hexena.Cmds.ReadMZ (run)
import qualified Hexena.Cmds.HexView (run)

-- | Record type defined to capture information recorded from
--   the command line.
data HexenaOpts = Block_Draw { src :: FilePath }
                | Calc_CV { src :: FilePath }
                | Classify_SVM { src :: FilePath }
                | Encapsulate_Exe { inputs :: [FilePath] }
                | Entropy_Calc { src :: FilePath
                                 , mean :: Bool }
                | Entropy_XPM { src :: FilePath }
                | Generate_Report { src :: FilePath }
                | Get_SVM_Data { inputs :: [FilePath]
                               , classifier :: Int }
                | Read_MZ { inputs :: [FilePath] }
                | Hex_View { src :: FilePath }
                | Calc_BlockEntropy { src :: FilePath }
                  deriving (Data, Typeable, Show, Eq)

block_draw :: HexenaOpts
block_draw = Block_Draw
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

calc_blockentropy :: HexenaOpts
calc_blockentropy = Calc_BlockEntropy
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

calc_cv :: HexenaOpts
calc_cv = Calc_CV
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

classify_svm :: HexenaOpts
classify_svm = Classify_SVM
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

encapsulate_exe :: HexenaOpts
encapsulate_exe = Encapsulate_Exe
    { inputs = def &= args
    }
    &= details [ "Examples:"
               , "TODO"
               ]
               
entropy_calc :: HexenaOpts
entropy_calc = Entropy_Calc
    { src = def &= typ "INPUT" &= argPos 0
    , mean = def &= name "m" &= help "Also calculate the mean value"
    }
    &= details [ "Examples:"
               , "TODO"
               ]

entropy_xpm :: HexenaOpts
entropy_xpm = Entropy_XPM
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

generate_report :: HexenaOpts
generate_report = Generate_Report
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

get_svm_data :: HexenaOpts
get_svm_data = Get_SVM_Data
    { inputs = def &= args
    , classifier = -1 
    }
    &= details [ "Examples:"
               , "TODO"
               ]


               
read_mz :: HexenaOpts
read_mz = Read_MZ
    { inputs = def &= args
    }
    &= details [ "Examples:"
               , "TODO"
               ]
               
hex_view :: HexenaOpts
hex_view = Hex_View
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

rootMode :: Mode (CmdArgs HexenaOpts)
rootMode = cmdArgsMode $ modes [ block_draw
                               , calc_blockentropy
                               , calc_cv
                               , classify_svm
                               , encapsulate_exe
                               , entropy_calc
                               , entropy_xpm
                               , generate_report
                               , get_svm_data
                               , read_mz
                               , hex_view
                               ]
           &= versionArg [explicit, name "version", name "v", summary _PROGRAM_INFO]
           &= summary (_PROGRAM_INFO)
           &= help _PROGRAM_ABOUT
           &= helpArg [explicit, name "help", name "h"]
           &= program _PROGRAM_NAME
    
-- | Option Handlers are the the change to weed out ugly, malformed or invalid data.
--   Option Handlers could be considered the warden, it would be a good place 
--   in the future to put a warden which would do considerably more src
--   checking before choosing to process.
--
--   Then sanitized arguments get passed to the program via 'exec'.
optionHandler :: HexenaOpts -> IO ()
optionHandler opts@Block_Draw{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Calc_BlockEntropy{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Calc_CV{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Classify_SVM{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Encapsulate_Exe{..} = do
    when (null inputs) $ putStrLn "warning: no files given"
    exec opts
optionHandler opts@Entropy_Calc{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Entropy_XPM{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Generate_Report{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Get_SVM_Data{..} = do
    when (null inputs) $ putStrLn "warning: no input files given"
    when (classifier `notElem` [-1, 1]) $ putStrLn "classifier for SVM must be either -1 (malicious) or 1 (benign)"
    exec opts
optionHandler opts@Read_MZ{..} = do
    when (null inputs) $ putStrLn "warning: no input files given"
    exec opts
optionHandler opts@Hex_View{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
    
-- | exec is the workhorse once optionHandler has properly transformed
--   the src data.
exec :: HexenaOpts -> IO ()
exec opts@Block_Draw{..} = Hexena.Cmds.BlockDraw.run src
exec opts@Calc_CV{..} = Hexena.Cmds.CalcCV.run src
exec opts@Calc_BlockEntropy{..} = Hexena.Cmds.CalcBlockEntropy.run src
exec opts@Classify_SVM{..} = Hexena.Cmds.ClassifySVM.run src
exec opts@Encapsulate_Exe{..} = Hexena.Cmds.EncapsulateEXE.run inputs
exec opts@Entropy_Calc{..} = Hexena.Cmds.EntropyCalc.run src mean
exec opts@Entropy_XPM{..} = Hexena.Cmds.EntropyXPM.run src
exec opts@Generate_Report{..} = Hexena.Cmds.GenerateReport.run src
exec opts@Get_SVM_Data{..} = Hexena.Cmds.GetSVMData.run classifier inputs
exec opts@Read_MZ{..} = Hexena.Cmds.ReadMZ.run inputs
exec opts@Hex_View{..} = Hexena.Cmds.HexView.run src
